package jmine.tec.proxy;

import java.util.HashSet;
import java.util.Set;

/**
 * @author MarceloT
 */
public abstract class AbstractProxyBuilder implements ProxyBuilder {

    private Set<Class<?>> interfaces = new HashSet<Class<?>>();

    /**
     * @return {@link Set}
     */
    public Set<Class<?>> getInterfaces() {
        return interfaces;
    }

    /**
     * Adds all interfaces that the given type implements directly or indirectly
     * @param type the type
     */
    public void addInterfacesFrom(Class<?> type) {
        addInterfacesFrom(type, this.interfaces);
    }

    /**
     * Adds all interfaces that the given type implements directly or indirectly
     * @param types the type
     */
    public final void setInterfaces(Class<?>... types) {
        for (Class<?> type : types) {
            addInterfacesFrom(type, this.interfaces);
        }
    }

    /**
     * Collects all interfaces as an array of interfaces
     * @return array of classes
     */
    public Class<?>[] collectAllInterfaces() {
        return extractAllInterfaces(Object.class, this.interfaces);
    }

    /**
     * @param type the type
     */
    public final void setMainType(Class<?> type) {
        addInterfacesFrom(type);
        this.doSetMainType(type);
    }

    /**
     * @param type the type
     */
    protected abstract void doSetMainType(Class<?> type);

    /**
     * @param type the type
     * @param allSets the set
     * @return array of classes
     */
    public static Class<?>[] extractAllInterfaces(Class<?> type, Set<Class<?>> allSets) {
        if (type == Object.class) {
            //end of invocation
            Class<?>[] asArray = new Class<?>[allSets.size()];
            return allSets.toArray(asArray);
        }
        addInterfacesFrom(type, allSets);
        return extractAllInterfaces(type.getSuperclass(), allSets);
    }

    /**
     * @param type the type
     * @param allSets the set with all interfaces
     */
    public static void addInterfacesFrom(Class<?> type, Set<Class<?>> allSets) {
        if (type == null) {
            return;
        }
        if (type.isInterface()) {
            allSets.add(type);
        }
        Class<?>[] interfaces2 = type.getInterfaces();
        for (Class<?> class1 : interfaces2) {
            addInterfacesFrom(class1, allSets);
        }
    }
}
